/*******************************************************************************
 * Copyright (c) 2013 QPark Consulting  S.a r.l.
 * 
 * This program and the accompanying materials are made available under the 
 * terms of the Eclipse Public License v1.0. 
 * The Eclipse Public License is available at 
 * http://www.eclipse.org/legal/epl-v10.html.
 * 
 * Contributors:
 *     Bernhard Hausen - Initial API and implementation
 *
 ******************************************************************************/
package com.qpark.maven.plugin.objectfactories;

import java.io.File;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.maven.plugin.logging.Log;

import com.qpark.maven.Util;
import com.qpark.maven.xmlbeans.ComplexType;
import com.qpark.maven.xmlbeans.ElementType;
import com.qpark.maven.xmlbeans.XsdsUtil;

/**
 * Generates a ServiceObjectFactory out of the XSDs containing elements.
 * @author bhausen
 */
public class ServiceObjectFactoryGenerator {
	/** Parsed complex types. */
	private final TreeSet<ElementType> elementTypes;
	/** The {@link Log}. */
	private final Log log;
	/** The {@link XsdsUtil}. */
	private final XsdsUtil xsds;
	/** The output directory. */
	private final File outputDirectory;

	/**
	 * @param config
	 * @param complexTypes
	 */
	public ServiceObjectFactoryGenerator(final XsdsUtil xsds,
			final File outputDirectory, final Log log) {
		this.xsds = xsds;
		this.outputDirectory = outputDirectory;
		this.log = log;
		this.elementTypes = xsds.getElementTypes();
	}

	public void generate() {
		this.log.debug("+generate");
		StringBuffer imports = new StringBuffer();
		TreeSet<String> contextPath = new TreeSet<String>();
		StringBuffer methods = new StringBuffer();
		StringBuffer objectFactories = new StringBuffer();

		imports.append("\nimport javax.xml.bind.JAXBElement;\n");
		// imports.append("\nimport org.springframework.stereotype.Component;\n");

		TreeMap<String, String> ofMap = new TreeMap<String, String>();
		TreeSet<String> methodNames = new TreeSet<String>();
		ComplexType ct;
		String ofName;
		String eName;
		String pOfName;
		String mName;
		for (ElementType element : this.elementTypes) {
			pOfName = element.getPackageName();
			contextPath.add(pOfName);
			ofName = ofMap.get(pOfName);
			if (ofName == null) {
				ofName = pOfName.replace('.', '_');
				objectFactories.append("\n\tprivate ").append(pOfName)
						.append(".ObjectFactory ").append(ofName)
						.append(" = new ").append(pOfName)
						.append(".ObjectFactory();");
				ofMap.put(pOfName, ofName);
			}
			eName = element.getClassNameObject();
			this.log.debug("new ComplexType " + element.getElement().getType());
			this.log.debug(" packagename "
					+ this.xsds.getPackageName(element.getElement().getType()
							.getName()));
			ct = new ComplexType(element.getElement().getType(), this.xsds);
			if (ct.getPackageName().length() > 0) {
				imports.append("\nimport ")
						.append(ct.getClassNameFullQualified()).append(";");
				mName = new StringBuffer(6 + eName.length()).append("create")
						.append(eName).toString();
				if (methodNames.contains(mName)) {
					mName = new StringBuffer(6 + eName.length()
							+ pOfName.length()).append("create")
							.append(Util.capitalizePackageName(pOfName))
							.append(eName).toString();
				}

				methodNames.add(mName);

				methods.append("\n\t/**");
				methods.append("\n\t * @param value a {@link ");
				methods.append(ct.getClassName());
				methods.append("}");
				methods.append("\n\t * @return a new {@link JAXBElement} containing a {@link ");
				methods.append(ct.getClassNameFullQualified());
				methods.append("}");

				methods.append("\n\t * @see ");
				methods.append(pOfName);
				methods.append(".ObjectFactory#create");
				methods.append(eName);
				methods.append("(");
				methods.append(ct.getClassNameFullQualified());
				methods.append(")");

				methods.append("\n\t */");
				methods.append("\n\tpublic JAXBElement<");
				methods.append(ct.getClassName());
				methods.append("> ").append(mName);
				methods.append("(final ");
				methods.append(ct.getClassName());
				methods.append(" value) {");

				methods.append("\n\t\treturn this.");
				methods.append(ofName).append(".create");
				methods.append(eName);
				methods.append("(value);");

				methods.append("\n\t}\n");
			}
		}

		File f = Util.getFile(this.outputDirectory,
				this.xsds.getBasePackageName(), "ServiceObjectFactory.java");
		this.log.info(new StringBuffer().append("Write ").append(
				f.getAbsolutePath()));

		try {

			StringBuffer sb = new StringBuffer(1024);
			sb.append("package ");
			sb.append(this.xsds.getBasePackageName());
			sb.append(";\n\n");
			sb.append("\n");
			sb.append(imports.toString());
			sb.append("\n");
			sb.append("\n/**\n * The ServiceObjectFactory contains the creation of all JAXBElement's defined\n * in the XSDs and extends the ModelObjectFactory.\n");
			sb.append(" * <pre>");
			sb.append(Util.getGeneratedAt());
			sb.append("</pre>\n");
			sb.append(" * @author bhausen\n");
			sb.append(" */\n");
			// sb.append("@Component\n");
			sb.append("public class ServiceObjectFactory extends ModelObjectFactory {");

			sb.append("\n\tpublic static final String CONTEXT_PATH_DEFINITON =\"");
			for (int i = 0; i < contextPath.size(); i++) {
				if (i > 0) {
					sb.append(":");
				}
				sb.append(contextPath.toArray()[i]);
			}
			sb.append("\";");

			sb.append(objectFactories.toString());
			sb.append("\n");
			sb.append(methods.toString());
			sb.append("}");
			Util.writeToFile(f, sb.toString());
		} catch (Exception e) {
			this.log.error(e.getMessage());
			e.printStackTrace();
		}
		this.log.debug("-generate");
	}

}
